package ar.com.framenet.base.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;

import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import ar.com.framenet.base.exception.ConfigurationException;
import ar.com.framenet.base.exception.UnhandledException;
import ar.com.framenet.base.util.XMLUtils;

/**
 * 
 * @author Samuel Cuellar
 */

@Component
@SuppressWarnings({ "rawtypes", "serial" })
public final class ApplicationConfiguration extends PropertyBean {

	/** Ultimo Documento de configuracion */
	private Document configurationDoc = null;

	/** Documento con el menu */
	private Document menuDoc = null;

	/** La instancia del singleton */
	private static ApplicationConfiguration theInstance = null;

	/**
	 * @param properties
	 */
	private ApplicationConfiguration(Map properties) {
		super(properties);
	}

	/**
	 * Constructor privado para evitar que se llame desde una clase externa
	 */
	private ApplicationConfiguration() {
		super();

		try {
			InputStream is = this.getClass().getClassLoader().getResourceAsStream("META-INF/config.xml");
			configurationDoc = XMLUtils.getDocumentFromStream(is);
			initialize();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Obtiene la instancia de esta clase Esto implementa el patron singleton,
	 * por lo que hay una sola instancia para todas las instancias de esta clase
	 * 
	 * @return ApplicationConfiguration
	 */
	public synchronized static ApplicationConfiguration getInstance() {
		if (null == ApplicationConfiguration.theInstance) {
			ApplicationConfiguration.theInstance = new ApplicationConfiguration();
		}
		return ApplicationConfiguration.theInstance;
	}

	/**
	 * Inizializa la configuracion.
	 * 
	 * @param configuration
	 * @throws ConfigurationException
	 */
	public void initialize(Document configuration) throws ConfigurationException {
		// Para futuros reseteos
		this.configurationDoc = configuration;
		// this.minVersion = (String)
		// this.getProperty(ApplicationConfiguration.MINIMAL_VERSION_PARAM);
		// this.currentVersion = (String)
		// this.getProperty(ApplicationConfiguration.CURRENT_VERSION_PARAM);

		// Solo para validar que esten correctamente seteados los parametros fundamentales
		// this.getValidatedProperty(ApplicationConfiguration.TIMEOUT_SESSION_PARAM);
		// this.getValidatedProperty(ApplicationConfiguration.SERVER_CHECKER_TIMEOUT_PARAM);
	}

	/**
	 * Relee el ultimo documento.
	 * 
	 * @param configuration
	 * @throws ConfigurationException
	 */
	public void initialize() throws ConfigurationException {
		if (this.configurationDoc == null) {
			throw new UnhandledException("El documento de configuracion no puede ser null si se quiere configurar la aplicacion. " + "Debe llamar al otro m�todo initialize()");
		}
		this.initialize(this.configurationDoc);
	}

	/**
	 * Obtiene una property validada
	 * 
	 * @param name
	 * @return
	 * @throws ConfigurationException
	 */
	public Object getValidatedProperty(String name) throws ConfigurationException {
		String value = (String) super.getProperty(name);
		if (value == null) {
			throw new ConfigurationException("No se encontro el parametro con nombre: " + name + " en la configuracion de la misma");
		}
		return value;
	}

	/**
	 * Obtiene una property validada
	 * 
	 * @param name
	 * @return
	 * @throws ConfigurationException
	 */
	public Document getMenuDocument() throws ConfigurationException {

		Element configElement = (Element) this.configurationDoc.getFirstChild();
		Element menuElement = (Element) configElement.getElementsByTagName(Constants.XML_MENU_ROOT).item(0);

		this.menuDoc = XMLUtils.getDocumentFromElement(menuElement);

		return this.menuDoc;
	}
}